/*-------------------------------------------------------------------------
 * drawElements C++ Base Library
 * -----------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Unique pointer.
 *//*--------------------------------------------------------------------*/

#include "deUniquePtr.hpp"

#include <exception>

namespace de
{

namespace
{

class Object
{
public:
	Object (bool& exists)
		: m_exists(exists)
	{
		m_exists = true;
	}

	~Object (void)
	{
		m_exists = false;
	}

private:
	bool& m_exists;
};

struct CustomDeleter
{
	CustomDeleter (bool* called) : m_called(called) {}

	void operator() (Object* ptr)
	{
		DE_TEST_ASSERT(!*m_called);
		delete ptr;
		*m_called = true;
	}

	bool* m_called;
};

MovePtr<Object> createObject (bool& exists)
{
	UniquePtr<Object> objectPtr(new Object(exists));
	return objectPtr.move();
}

} // anonymous

void UniquePtr_selfTest (void)
{
	// Basic test.
	{
		bool exists = false;
		{
			UniquePtr<Object> ptr(new Object(exists));
			DE_TEST_ASSERT(exists);
			DE_TEST_ASSERT(ptr.get() != DE_NULL);
		}
		DE_TEST_ASSERT(!exists);
	}

	// Exception test.
	{
		bool exists = false;
		try
		{
			UniquePtr<Object> ptr(new Object(exists));
			DE_TEST_ASSERT(exists);
			DE_TEST_ASSERT(ptr.get() != DE_NULL);
			throw std::exception();
		}
		catch (const std::exception&)
		{
			DE_TEST_ASSERT(!exists);
		}
		DE_TEST_ASSERT(!exists);
	}

	// Expression test.
	{
		bool exists = false;
		bool test	= (UniquePtr<Object>(new Object(exists))).get() != DE_NULL && exists;
		DE_TEST_ASSERT(!exists);
		DE_TEST_ASSERT(test);
	}

	// Custom deleter.
	{
		bool exists = false;
		bool deleterCalled = false;
		{
			UniquePtr<Object, CustomDeleter> ptr(new Object(exists), CustomDeleter(&deleterCalled));
			DE_TEST_ASSERT(exists);
			DE_TEST_ASSERT(!deleterCalled);
			DE_TEST_ASSERT(ptr.get() != DE_NULL);
		}
		DE_TEST_ASSERT(!exists);
		DE_TEST_ASSERT(deleterCalled);
	}

	// MovePtr -> MovePtr moving
	{
		bool exists = false;
		MovePtr<Object> ptr(new Object(exists));
		DE_TEST_ASSERT(exists);
		{
			MovePtr<Object> ptr2 = ptr;
			DE_TEST_ASSERT(exists);
			// Ownership moved to ptr2, should be deleted when ptr2 goes out of scope.
		}
		DE_TEST_ASSERT(!exists);
	}

	// UniquePtr -> MovePtr moving
	{
		bool exists = false;
		UniquePtr<Object> ptr(new Object(exists));
		DE_TEST_ASSERT(exists);
		{
			MovePtr<Object> ptr2 = ptr.move();
			DE_TEST_ASSERT(exists);
			// Ownership moved to ptr2, should be deleted when ptr2 goes out of scope.
		}
		DE_TEST_ASSERT(!exists);
	}

	// MovePtr -> UniquePtr moving
	{
		bool exists = false;
		{
			UniquePtr<Object> ptr(createObject(exists));
			DE_TEST_ASSERT(exists);
		}
		DE_TEST_ASSERT(!exists);
	}
	
	// MovePtr assignment
	{
		bool exists1 = false;
		bool exists2 = false;
		MovePtr<Object> ptr1(new Object(exists1));
		MovePtr<Object> ptr2(new Object(exists2));
		ptr1 = ptr2;
		DE_TEST_ASSERT(!exists1);
		DE_TEST_ASSERT(exists2);
	}

	// MovePtr stealing
	{
		bool exists = false;
		Object* raw = DE_NULL;
		{
			MovePtr<Object> ptr1(new Object(exists));
			raw = ptr1.release();
			DE_TEST_ASSERT(raw != DE_NULL);
			DE_TEST_ASSERT(ptr1.get() == DE_NULL);
			DE_TEST_ASSERT(exists);
		}
		DE_TEST_ASSERT(exists);
		delete raw;
		DE_TEST_ASSERT(!exists);
	}

	// Null MovePtr and assigning to it.
	{
		bool exists = false;
		{
			MovePtr<Object> ptr1;
			DE_TEST_ASSERT(ptr1.get() == DE_NULL);
			MovePtr<Object> ptr2(new Object(exists));
			ptr1 = ptr2;
			DE_TEST_ASSERT(exists);
			DE_TEST_ASSERT(ptr1.get() != DE_NULL);
			DE_TEST_ASSERT(ptr2.get() == DE_NULL);
		}
		DE_TEST_ASSERT(!exists);
	}
	
#if 0
	// UniquePtr assignment or copy construction should not compile. This
	// piece of code is intentionally commented out. To manually test that
	// copying a UniquePtr is statically forbidden, uncomment and try to
	// compile.
	{
		bool exists = false;
		UniquePtr<Object> ptr(new Object(exists));
		{
			UniquePtr<Object> ptr2(ptr);
			DE_TEST_ASSERT(exists);
		}
		DE_TEST_ASSERT(!exists);
	}
#endif
}

} // de
